/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


// win_input.cpp -- Windows mouse code


#include "../client/client.h"
#include "win_local.h"


#define MAX_MOUSE_BUTTONS		5

typedef struct {
	bool			initialized;
	bool			active;

	POINT			originalPos;

	int				windowCenterX;
	int				windowCenterY;

	int				buttonState;
} mouseVars_t;

static mouseVars_t	in_mouseVars;

static cvar_t *		in_mouse;


/*
 ==================
 IN_ActivateMouse
 ==================
*/
static void IN_ActivateMouse (void){

	RECT	rect;
	int		screenWidth, screenHeight;

	if (in_mouseVars.active)
		return;
	in_mouseVars.active = true;

	GetCursorPos(&in_mouseVars.originalPos);

	GetWindowRect(sys.hWndMain, &rect);

	screenWidth = GetSystemMetrics(SM_CXSCREEN);
	screenHeight = GetSystemMetrics(SM_CYSCREEN);

	if (rect.left < 0)
		rect.left = 0;
	if (rect.top < 0)
		rect.top = 0;
	if (rect.right >= screenWidth)
		rect.right = screenWidth - 1;
	if (rect.bottom >= screenHeight)
		rect.bottom = screenHeight - 1;

	in_mouseVars.windowCenterX = (rect.left + rect.right) / 2;
	in_mouseVars.windowCenterY = (rect.top + rect.bottom) / 2;

	SetCursorPos(in_mouseVars.windowCenterX, in_mouseVars.windowCenterY);

	while (ShowCursor(FALSE) >= 0)
		;

	ClipCursor(&rect);
	SetCapture(sys.hWndMain);
}

/*
 ==================
 IN_DeactivateMouse
 ==================
*/
static void IN_DeactivateMouse (void){

	if (!in_mouseVars.active)
		return;
	in_mouseVars.active = false;

	ReleaseCapture();
	ClipCursor(NULL);

	while (ShowCursor(TRUE) < 0)
		;

	SetCursorPos(in_mouseVars.originalPos.x, in_mouseVars.originalPos.y);
}

/*
 ==================
 IN_MouseButtonEvent
 ==================
*/
void IN_MouseButtonEvent (int buttonState){

	int		i;

	if (!in_mouseVars.initialized)
		return;

	if (in_mouseVars.buttonState == buttonState)
		return;

	for (i = 0; i < MAX_MOUSE_BUTTONS; i++){
		if ((buttonState & BIT(i)) && !(in_mouseVars.buttonState & BIT(i)))
			Key_Event(K_MOUSE1 + i, true, sys.msgTime);

		if (!(buttonState & BIT(i)) && (in_mouseVars.buttonState & BIT(i)))
			Key_Event(K_MOUSE1 + i, false, sys.msgTime);
	}

	in_mouseVars.buttonState = buttonState;
}

/*
 ==================
 IN_MouseWheelEvent
 ==================
*/
void IN_MouseWheelEvent (int wheelDelta){

	int		i;

	if (!in_mouseVars.initialized)
		return;

	if (!wheelDelta)
		return;

	if (wheelDelta > 0){
		for (i = 0; i < wheelDelta; i++){
			Key_Event(K_MWHEELUP, true, sys.msgTime);
			Key_Event(K_MWHEELUP, false, sys.msgTime);
		}
	}
	else {
		for (i = 0; i > wheelDelta; i--){
			Key_Event(K_MWHEELDOWN, true, sys.msgTime);
			Key_Event(K_MWHEELDOWN, false, sys.msgTime);
		}
	}
}

/*
 ==================
 IN_MouseFrame
 ==================
*/
void IN_MouseFrame (void){

	POINT	pos;
	int		x, y;

	if (!in_mouseVars.initialized)
		return;

	if (!sys.active || !sys.hWndMain){
		IN_DeactivateMouse();
		return;
	}

	if (!sys.isFullscreen && (Key_GetCatchers() & KEYCATCH_CONSOLE)){
		IN_DeactivateMouse();
		return;
	}

	IN_ActivateMouse();

	// Find mouse movement
	if (!GetCursorPos(&pos))
		return;

	x = pos.x - in_mouseVars.windowCenterX;
	y = pos.y - in_mouseVars.windowCenterY;

	if (!x && !y)
		return;

	// Force the mouse to the center, so there's room to move
	SetCursorPos(in_mouseVars.windowCenterX, in_mouseVars.windowCenterY);

	// Move the GUI cursor or accumulate mouse movement
	IN_MouseMove(x, y);
}

/*
 ==================
 IN_ClearStates
 ==================
*/
void IN_ClearStates (void){

	IN_ClearMouseMovement();

	in_mouseVars.buttonState = 0;
}

/*
 ==================
 IN_Activate

 Called when the main window gains or loses focus.
 The window may have been destroyed and recreated between a deactivate and an
 activate.
 ==================
*/
void IN_Activate (bool active){

	if (!in_mouseVars.initialized)
		return;

	if (active)
		IN_ActivateMouse();
	else
		IN_DeactivateMouse();
}

/*
 ==================
 IN_Restart_f
 ==================
*/
static void IN_Restart_f (void){

	IN_Shutdown();
	IN_Init();
}

/*
 ==================
 IN_Init
 ==================
*/
void IN_Init (void){

	Com_Printf("------- Input Initialization -------\n");

	// Register our variables and commands
    in_mouse = CVar_Get("in_mouse", "1", CVAR_ARCHIVE | CVAR_LATCH, "Enable mouse input");

	Cmd_AddCommand("restartInput", IN_Restart_f, "Restarts the input subsystem");

	// Activate the mouse if desired
	if (!in_mouse->integerValue)
		Com_Printf("Mouse control not active\n");
	else {
		if (!GetSystemMetrics(SM_MOUSEPRESENT))
			Com_Printf("No mouse detected\n");
		else {
			Com_Printf("Mouse detected\n");

			in_mouseVars.initialized = true;

			IN_ActivateMouse();
		}
	}

	Com_Printf("------------------------------------\n");
}

/*
 ==================
 IN_Shutdown
 ==================
*/
void IN_Shutdown (void){

	int		i;

	Cmd_RemoveCommand("restartInput");

	// Clear accumulated mouse movement
	IN_ClearMouseMovement();

	// Deactivate the mouse
	if (!in_mouseVars.initialized)
		return;

	for (i = 0; i < MAX_MOUSE_BUTTONS; i++){
		if (in_mouseVars.buttonState & BIT(i))
			Key_Event(K_MOUSE1 + i, false, 0);
	}

	IN_DeactivateMouse();

	memset(&in_mouseVars, 0, sizeof(mouseVars_t));
}
